import React, { Component } from 'react';
import {
Typography, FormControl, Select, MenuItem, Link,
InputLabel, TextField, Fab, FormControlLabel, Checkbox,
Button, Switch, Tooltip
} from "@material-ui/core";
import {Slider} from '@material-ui/lab';
import "./DetailView.css";
import "./ParamView.css";
import {fillCanvasPixelsWithRgbAndAlpha, fillCanvasPixelsWithGreyAndAlpha,
getImageData, getRgbFromCanvasRgba, getGrayFromCanvasRgba}
from '../DrawingHelper.js';
import * as d3 from 'd3';
import ActivationView from './ActivationView'
import NeuronOverlayCanvas from './NeuronOverlayCanvas'
import SecondaryPreview from './SecondaryPreview'
import {objectiveTypes, loadStates} from '../LucidJS/src/optvis/renderer.js';
import { saveAs } from 'file-saver';
import {ttActivationView} from '../strings'
import { scdryPrvMode } from '../Home0';
/**
* Component containing detail visualizations for current layer.
*/
class DetailView extends Component {
constructor(props){
super(props);
this.imageCanvas = React.createRef();
this.imageCanvasSecondary = React.createRef();
this.imageCanvasStyle = React.createRef();
this.state = {
prevWidth: undefined,
}
this.scdryEnabledModes = [
scdryPrvMode.COMPARE,
scdryPrvMode.NEURON,
//scdryPrvMode.PAINT,
//scdryPrvMode.ACT_ADJUST,
//scdryPrvMode.STYLE,
]
}
dispatchResizeEvent = () => {
let ev = new Event('resizeActivationView');
window.dispatchEvent(ev);
}
/**
* Dynamically adapt dimensions of image preview.
*/
calcPrevWidth = () => {
let wW = window.innerWidth;
let wH = window.innerHeight;
const wR = wW / 1680;
const hR = wH / 1050;
let prevWidth = 224;
if(wR > hR) {
prevWidth = wH / 4.5;
} else {
prevWidth = wW / 7.5;
}
return prevWidth;
}
/**
* Trigger rendering with new width.
*/
reRender = () => {
this.setState({prevWidth: this.calcPrevWidth()});
}
componentDidMount(props){
d3.select('.canvasContainer').select('.layout-pane-primary')
.style('overflow', 'hidden');
window.addEventListener("resize", this.reRender);
this.dispatchResizeEvent();
}
componentWillUnmount(props){
window.removeEventListener("resize", this.reRender);
}
detail(){
let w = this.props.inputSize;
let h = this.props.inputSize;
let canvases = null;
window.requestAnimationFrame(() => {
if(this.imageCanvas.current && this.props.currentInput){
const [b, inW, inH, ch] = this.props.inputShape;
this.drawPixelsToCanvas(this.props.currentInput,
this.imageCanvas.current, w, h, ch===3 ? true : false);
}
if(this.imageCanvasSecondary.current && this.props.lastInput) {
const width = this.props.inputSize;
const height = this.props.inputSize;
const [b, inW, inH, ch] = this.props.lastInputShape;
this.imageCanvasSecondary.current.getContext("2d").putImageData(
this.getImageDataFromPixelData(
this.props.lastInput, width, height, ch===3 ? true : false), 0, 0
)
}
if(this.imageCanvasStyle.current) {
const width = this.props.inputSize;
const height = this.props.inputSize;
console.log(width, height);
if(this.props.styleImage) {
const [b, inW, inH, ch] = this.props.styleImageShape;
this.imageCanvasStyle.current.getContext("2d").putImageData(
this.getImageDataFromPixelData(
this.props.styleImage, width, height, ch===3 ? true : false, 255),
0, 0
)
} else {
this.imageCanvasStyle.current.getContext("2d")
.clearRect(0, 0, width, height);
}
}
});
const prevWidth = this.state.prevWidth ?
this.state.prevWidth : this.calcPrevWidth();
const activationTitle = this.props.selectedLayer ?
'Activations for layer "'+this.props.selectedLayer+'"' :
'Activations';
return(
<div style={{width: "100%", height: "100%",
display: 'flex', flexDirection: 'row', overflow: 'hidden'}}>
<div style={{width: "35%", height: "100%",
flex:"0 1 auto", borderRight:"1px solid darkgray", display: "flex",
flexDirection: "column", justifyContent: "space-evenly"}}
className="inputPreviewContainer">
<div style={{padding: "15px"}}>
<div className="optimPreviewCanvasContainer"
style={{height: '100%'}}>
<div id="livePreview" >
<div style={{display:'flex', justifyContent:'center',
alignItems:'center'}}>
<canvas className="optimPreviewCanvas"
ref={this.imageCanvas}
width={this.props.inputSize}
height={this.props.inputSize}
style={{width:prevWidth, height:prevWidth}}/>
</div>
<div className="splitParamGroup">
<Button variant='contained'
className="wholeWidth"
onClick={() => {
if(this.imageCanvas.current) {
this.imageCanvas.current.toBlob(function(blob) {
saveAs(blob, "lucidImage.png");
});
}
}}
>Save</Button>
<Button className="wholeWidth"
variant='contained'
onClick={(e,v) => {
this.props.onReset();
}}>Reset</Button>
</div>
</div>
</div>
</div>
<div style={{padding: "15px"}}>
<div style={{height: '100%'}}
className="optimPreviewCanvasContainer">
<SecondaryPreview
showHelp={this.props.showHelp}
selectedLayer={this.props.selectedLayer}
selectedChannel={this.props.selectedChannel}
imageCanvasSecondary={this.imageCanvasSecondary}
imageCanvasStyle={this.imageCanvasStyle}
inputSize={this.props.inputSize}
activations={this.props.detailActivations}
activationShape={this.props.activationShape}
detailActivationStats={this.props.detailActivationStats}
prevWidth={prevWidth}
selectedNeuron={this.props.selectedNeuron}
neuronChanged={this.props.neuronChanged}
activationMods={this.props.activationMods}
activationsModified={this.props.activationsModified}
prvMode={this.props.prvMode}
prvModeChanged={this.props.prvModeChanged}
styleImage={this.props.styleImage}
uploadedStyleImage={this.props.uploadedStyleImage}
layerList={this.props.layerList}
styleLayers={this.props.styleLayers}
styleLayerChanged={this.props.styleLayerChanged}
enabledModes={this.scdryEnabledModes}
/>
</div>
</div>
</div>
<div style={{height: '100%', width: '65%', flex:"0 1 auto",
display: 'flex', flexFlow: 'column'}}>
<Tooltip title={this.props.showHelp ? ttActivationView : ''}>
<div
style={{flex: "0 1 auto"}}
className="titleContainer">
<h4 className="titleText">{activationTitle}</h4>
</div>
</Tooltip>
<div style={{height: '50%', flex: "1 1 auto"}}
className="activationCanvasContainer">
<ActivationView
activationShape={this.props.activationShape}
channelNumber={this.props.channelNumber}
selectedChannel={this.props.selectedChannel}
activations={this.props.activations}
onClickFeatureMap={(selectedInd) => {
this.props.channelChanged(selectedInd);
}}
/>
</div>
</div>
</div>);
}
render(){
return this.detail();
}
/**
* Fills canvas with pixel data.
* @param {*} pixelData Raw pixel array
* @param {*} canvas Canvas to draw on to
* @param {*} w width of pixel data
* @param {*} h height of pixel data
* @param {boolean} rgb rgb or greyscale status of pixel data
* @param {*} channel channel to take from pixel data in case multiple channels are contained
*/
drawPixelsToCanvas(pixelData, canvas, w, h, rgb, channel=0){
let canvCtx = canvas.getContext("2d");
const cw = canvas.width;
const ch = canvas.height;
const wratio = cw / w;
const hratio = ch / h;
if(w !== cw || h !== ch) {
let tempCanvas = document.createElement("canvas");
tempCanvas.width = w;
tempCanvas.height = h;
let tempCtx = tempCanvas.getContext('2d');
let imData = tempCtx.createImageData(w, h);
if(rgb){
fillCanvasPixelsWithRgbAndAlpha(imData.data, pixelData, w, h, channel);
} else {
fillCanvasPixelsWithGreyAndAlpha(imData.data, pixelData, w, h, channel);
}
tempCtx.putImageData(imData, 0, 0);
canvCtx.scale(wratio, hratio);
canvCtx.drawImage(tempCanvas, 0, 0);
canvCtx.scale(1/wratio, 1/hratio);
} else {
let imData = canvCtx.createImageData(w, h);
if(rgb){
fillCanvasPixelsWithRgbAndAlpha(imData.data, pixelData, w, h, channel);
} else {
fillCanvasPixelsWithGreyAndAlpha(imData.data, pixelData, w, h, channel);
}
canvCtx.putImageData(imData, 0, 0);
}
}
/**
* Converts array of pixel values into ImageData object.
*/
getImageDataFromPixelData(pixelData, w, h, rgb, mult=1, channel=0){
let tempCanvas = document.createElement("canvas");
tempCanvas.width = w;
tempCanvas.height = h;
let tempCtx = tempCanvas.getContext('2d');
let imData = tempCtx.createImageData(w, h);
channel = 0;
if(rgb){
fillCanvasPixelsWithRgbAndAlpha(
imData.data, pixelData, w, h, channel, mult);
} else {
fillCanvasPixelsWithGreyAndAlpha(
imData.data, pixelData, w, h, channel, mult);
}
return imData
}
}
export default DetailView;